package blog.generic;

import java.util.ArrayList;
import java.util.List;

/**
 * java与kotlin不支持直接型变，
 * 型变包括协变与逆变，相当于<? extend A>和<? super B>
 * Producer-Extends-out-get, Consumer-Super-in-写入-set
 * 协变 out == <? extend A>,逆变 in == <? super B>
 */
public class TypeParamsJava {

    public void test() {
        //java 可以通过<? extend xxx> 在声明变量处上界通配符实现协变
        //协变不能add，可以get(只读不能写)
        ArrayList<Integer> integers = new ArrayList<>();
        integers.add(1);
        ArrayList<? extends Number> list1 = integers;
        Number number = list1.get(0);//可以取值，上界类型
//        list1.add(new Integer(1));error

        ArrayList<? super Number> list2 = new ArrayList<Object>();
        //逆变可以add,因此这种叫消费者
        list2.add(new Integer(2));
        //当然，java中逆变也可以取值，只是都是Object类型，没有意义
        Object object = list2.get(0);


        //? 也是通配符，但与E，T等其他区别在于，?表示未知类型，因此只能在声明变量处型变时用到，声明类或者接口，方法时只能使用有意义的通配符，
        // E,T等通配符也只能用于声明类或者接口，方法时，不能在声明变量处型变使用
        //? 相当于? extend Object , kt中相似的表示为*
        List<?> list3 = new ArrayList<Integer>();

    }

    //注意，这里不是声明变量时的协变，仅仅只是设置泛型类型参数为某个类型的子集
    public <T extends Number & Comparable> void test1() {

    }

    //同上，而且不是型变，不存在super通配符，
    // kt中如果是单个限定可以是<T :Number>,
    // 如果是多个限定:<T> where T : Number,T:Comparable<T>
    class Inner<T extends Number & Comparable> {

    }

    //由于 Java 中的编译时泛型存在类型擦除的情况，任何在运行时需要知道泛型确切类型信息的操作都没法用了
//    private <T> void test1(Number n){
//        if (n instanceof T){
//            error
//        }
//    }
    //解决方法：再传入一个Class<T>
    private <T> void test1(Number n, Class<T> clz) {
        if (clz.isInstance(n)) {

        }
    }


}
